package com.sharer.last.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.Netty4ClientHttpRequestFactory;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.client.DefaultOAuth2ClientContext;
import org.springframework.security.oauth2.client.OAuth2RestTemplate;
import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails;
import org.springframework.security.oauth2.common.AuthenticationScheme;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.JdbcAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.util.Arrays;

/**
 * 授权服务器配置
 *
 * @ClassName AuthorizationServerConfig
 * @Author WangJin
 * @Date 2023/05/09/14:13
 * @Description 也可以继承AuthorizationServerConfigurer
 * @Version 1.0
 */
@Configuration
@EnableAuthorizationServer// 启动授权服务器
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    @Resource
    TokenStore tokenStore;

    @Resource
    private AuthenticationManager authenticationManager;

    @Resource
    DataSource dataSource;

    @Resource
    PasswordEncoder passwordEncoder;

    @Resource
    private JwtAccessTokenConverter jwtAccessTokenConverter;

    @Resource
    CustomAdditionalInformation customAdditionalInformation;
    /**
     * 配置授权服务器的安全性
     *
     * @param security
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        // allowFormAuthenticationForClients() 允许客户端进行表单身份验证,使用表单认证申请令牌
        security
                //对应/oauth/token_key 公开，获取公钥需要访问该端点
                .tokenKeyAccess("permitAll()")
                //对应/oauth/check_token ，路径公开，校验Token需要请求该端点 验证token放开权限
                .checkTokenAccess("permitAll()")
                //允许表单认证
                .allowFormAuthenticationForClients()
        ;
    }

    @Bean
    public JdbcClientDetailsService jdbcClientDetailsService() {
        JdbcClientDetailsService jdbcClientDetailsService = new JdbcClientDetailsService(dataSource);
        //数据库的秘钥使用了PasswordEncoder加密
        jdbcClientDetailsService.setPasswordEncoder(passwordEncoder);
        return jdbcClientDetailsService;
    }

    /**
     * 配置客户端属性
     *
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.withClientDetails(jdbcClientDetailsService());
        //.autoApprove(true)//false跳转到授权页面//加上验证回调地址.redirectUris("http://www.baidu.com")
    }

    /**
     * 配置授权服务器端点的非安全特性:如token、store
     *
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        // tokenGranter：如果设置了，那么授权将会交由你完全掌控，并忽略上面的几个属性，一般用作拓展用途，即标准的四种授权模式已经满足不了你的需求的时候，才会考虑使用这个
        // authorizationCodeServices 授权码模式需要
        // authenticationManager 认证管理器,密码模式需要
        // tokenEnhancer 自定义生成令牌
        // exceptionTranslator 自定义异常处理
        // reuseRefreshTokens 是否重复使用 refresh_token
        endpoints
                // 授权码模式需要
                .authorizationCodeServices(authorizationCodeServices(dataSource))
                // 请求方式
                .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST,HttpMethod.PUT,HttpMethod.DELETE)
                // 密码模式需要
                .authenticationManager(authenticationManager)
                // 指定token存储位置
//                .tokenStore(tokenStore)
                .tokenServices(tokenService())
                .accessTokenConverter(jwtAccessTokenConverter)
        ;
    }
    //令牌管理服务 TODO 是否必须
    @Bean
    public AuthorizationServerTokenServices tokenService() {
        DefaultTokenServices service = new DefaultTokenServices();
        service.setSupportRefreshToken(true);//支持刷新令牌
        service.setTokenStore(tokenStore);//令牌存储策略
        service.setAccessTokenValiditySeconds(2592000); // 令牌默认有效期2小时
        service.setRefreshTokenValiditySeconds(259200); // 刷新令牌默认有效期3天

        //令牌增强
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        tokenEnhancerChain.setTokenEnhancers(Arrays.asList(jwtAccessTokenConverter, customAdditionalInformation));
        service.setTokenEnhancer(tokenEnhancerChain);
        return service;
    }

    @Bean
    public AuthorizationCodeServices authorizationCodeServices(DataSource dataSource) {
        return new JdbcAuthorizationCodeServices(dataSource);
    }

    @Bean("clientCredentialsResourceDetails")
    public ClientCredentialsResourceDetails resourceDetails() {
        ClientCredentialsResourceDetails details = new ClientCredentialsResourceDetails();
        //http://localhost:${server.port}/oauth/token
        details.setAccessTokenUri("http://localhost:8090/oauth/token");
        details.setClientId("client");
        details.setClientSecret("123456");
        details.setAuthenticationScheme(AuthenticationScheme.header);
        return details;
    }

    @Bean("oAuth2RestTemplate")
    public OAuth2RestTemplate oAuth2RestTemplate() {
        final OAuth2RestTemplate oAuth2RestTemplate = new OAuth2RestTemplate(resourceDetails(), new DefaultOAuth2ClientContext());
        //default: SimpleClientHttpRequestFactory
        oAuth2RestTemplate.setRequestFactory(new Netty4ClientHttpRequestFactory());
        return oAuth2RestTemplate;

    }
}


